package com.kara.woodAgent.agent.graph;

import cn.hutool.core.util.StrUtil;
import com.kara.woodAgent.agent.graph.communicate.Channel;
import com.kara.woodAgent.agent.graph.communicate.StepImage;
import com.kara.woodAgent.agent.graph.exception.InnerCheckException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;

/**
 * @author tzy
 * @version 1.0
 * @project wood_agent
 * @description 基础流程图
 * @date 2025/6/4 10:40:02
 */
public class BaseGraph<T extends GraphContext> {

    private static final Logger log = LoggerFactory.getLogger(BaseGraph.class);

    public Node<T> START_NODE;
    public String START_NODE_NAME;

    public static final String END_NODE = "__end_node__";

    private final ConcurrentHashMap<String, Node<T>> nodes = new ConcurrentHashMap<>();


    private Supplier<Channel> channelSupplier = Channel::new;

    public BaseGraph<T> addNode(String nodeName, Node<T> node) {
        nodes.put(nodeName, node);
        return this;
    }

    public BaseGraph<T> addStartNode(String nodeName, Node<T> node) {
        nodes.put(nodeName, node);
        this.START_NODE = node;
        this.START_NODE_NAME = nodeName;
        return this;
    }

    public BaseGraph<T> channelSupplier(Supplier<Channel> supplier) {
        this.channelSupplier = supplier;
        return this;
    }

//    public RunningGraph<T> build(T context) {
//
//        if (START_NODE == null) {
//            throw new RuntimeException("START_NODE is null");
//        }
//        Channel channel = channelSupplier.get();
//        context.setChannel(channel);
//        return new BaseRunningGraph<T>(START_NODE_NAME, this, context, channel);
//    }


    //包和子类
    protected void execute(T context, BaseRunningGraph<T> step) {
        boolean loop = true;
        Node<T> currentNode = nodes.get(step.nodeName());
        String currentNodeName = step.nodeName();
        if (currentNode == null) {
            throw new RuntimeException("node not found: " + step.nodeName());
        }

        while (loop) {
            log.debug("current node: {}", currentNodeName);
            Next next;
            try {
                if (currentNode instanceof GraphNode<T> subGraphNode) {

                    next = subGraphNode.executeGraph(context, step);
                } else {
                    next = currentNode.execute(context);
                }
            } catch (InnerCheckException e) {
                //内部受检
                step.clear();
                return;
            }

            //停止已经
            if (step.isStop()) {
                step.clear();
                break;
            }

            if (next.response != null) {
                try {
                    step.execute(next.response);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }


            if (END_NODE.equals(next.name)) {
                loop = false;
                //执行完成修改状态
                step.finish();
                step.clear();
            } else {
                step.nodeName(next.name);
                currentNode = nodes.get(step.nodeName());
                currentNodeName = step.nodeName();
                if (currentNode == null) {
                    throw new RuntimeException("node not found:" + next.name);
                }
            }
        }

        log.debug("graph run over.........");
    }


    public RunningGraph<T> build(StepImage<T> image) {

//		if (image == null) {
//			//新建一个
//			return build(context);
//		}
        if (START_NODE == null) {
            throw new RuntimeException("START_NODE is null");
        }
        T context = image.getContext();

        String graphClassName = image.getGraphClassName();
        String className = this.getClass().getName();
        if (!StrUtil.equals(graphClassName, className)) {
            throw new RuntimeException("graph class name not match:" + graphClassName + " & " + className);
        }

        Channel channel = channelSupplier.get();
        context.setChannel(channel);
        BaseRunningGraph<T> baseStep = new BaseRunningGraph<>(image.getNode(), this, context, channel);
        if (!StepImage.STATUS_INIT.equals(image.getStatus())) {
            baseStep.saveImage(image);
        }
        return baseStep;

    }


    public StepImage<T> createImage(T context) {
        StepImage<T> image = StepImage.from(context);

        image.setNode(START_NODE_NAME);
        image.setGraphClassName(this.getClass().getName());
        image.setStatus(StepImage.STATUS_INIT);
        return image;
    }
}
